﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Toolkit.Web.ExtensionMethods
{
    public static class HttpRequestExtensions
    {
        /// <summary>
        /// Tries to retrieve a value type from request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection.</param>
        /// <returns>Converted value associated with the given key or default value if conversion fails.</returns>
        public static T GetValue<T>(this HttpRequest request, string key) where T : struct
        {
            return request.GetValue<T>(key, default(T));
        }

        /// <summary>
        /// Tries to retrieve a value type from request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection.</param>
        /// <param name="defaultValue">Optional default value to return if conversion fails or no entry in the request collection exists.</param>
        /// <returns>Converted value associated with the given key or default value if conversion fails.</returns>
        public static T GetValue<T>(this HttpRequest request, string key, Nullable<T> defaultValue) where T : struct
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            string value = request[key];

            if (string.IsNullOrEmpty(value))
            {
                if (defaultValue.HasValue)
                {
                    return defaultValue.Value;
                }
                else
                {
                    return default(T);
                }
            }

            return value.Convert<T>(default(T));
        }

        /// <summary>
        /// Tries to retrieve a nullable value type from request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection.</param>
        /// <returns>Converted value associated with the given key or default value if conversion fails.</returns>
        public static Nullable<T> GetNullableValue<T>(this HttpRequest request, string key) where T : struct
        {
            return request.GetNullableValue<T>(key, null);
        }

        /// <summary>
        /// Tries to retrieve a nullable value type from request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection.</param>
        /// <param name="defaultValue">Optional default value to return if conversion fails or no entry in the request collection exists.</param>
        /// <returns>Converted value associated with the given key or default value if conversion fails.</returns>
        public static Nullable<T> GetNullableValue<T>(this HttpRequest request, string key, Nullable<T> defaultValue) where T : struct
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            string value = request[key];

            if (string.IsNullOrEmpty(value))
            {
                return defaultValue;
            }

            return value.ConvertNullable<T>(null);
        }

        /// <summary>
        /// Tries to retrieve string from request collection.
        /// </summary>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection.</param>
        /// <returns>Value associated with the given key or default value if conversion fails.</returns>
        public static string GetValue(this HttpRequest request, string key)
        {
            return request.GetValue(key, null);
        }

        /// <summary>
        /// Tries to retrieve string from request collection.
        /// </summary>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection.</param>
        /// <param name="defaultValue">Optional default value to return if no entry in the request collection exists.</param>
        /// <returns>Value associated with the given key or default value if conversion fails.</returns>
        public static string GetValue(this HttpRequest request, string key, string defaultValue)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            string value = request[key];

            if (value == null)
            {
                return defaultValue;
            }

            return value;
        }

        /// <summary>
        /// Tries to retrieve a list of value types from a request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert each item in the collection to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection (not including the index).</param>
        /// <returns>Converted list of values associated with the given key or an empty collection if no values are found.</returns>
        public static IEnumerable<T> GetValues<T>(this HttpRequest request, string key) where T : struct
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            List<T> values = new List<T>();
            int index = 0;
            int maxAttempts = 10000;
            string keyWithIndex = null;

            while (index < maxAttempts)
            {
                keyWithIndex = string.Format("{0}[{1}]", key, index);

                if (!request.Params.AllKeys.Contains(keyWithIndex))
                {
                    break;
                }

                values.Add(request.GetValue<T>(keyWithIndex));

                index += 1;
            }

            return values;
        }

        /// <summary>
        /// Tries to retrieve a list of nullable value types from a request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert each item in the collection to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection (not including the index).</param>
        /// <returns>Converted list of values associated with the given key or an empty collection if no values are found.</returns>
        public static IEnumerable<Nullable<T>> GetNullableValues<T>(this HttpRequest request, string key) where T : struct
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            List<Nullable<T>> values = new List<Nullable<T>>();
            int index = 0;
            int maxAttempts = 10000;
            string keyWithIndex = null;

            while (index < maxAttempts)
            {
                keyWithIndex = string.Format("{0}[{1}]", key, index);

                if (!request.Params.AllKeys.Contains(keyWithIndex))
                {
                    break;
                }

                values.Add(request.GetNullableValue<T>(keyWithIndex));

                index += 1;
            }

            return values;
        }

        /// <summary>
        /// Tries to retrieve a list of strings from a request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert each item in the collection to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection (not including the index).</param>
        /// <returns>Converted list of values associated with the given key or an empty collection if no values are found.</returns>
        public static IEnumerable<string> GetValues(this HttpRequest request, string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            List<string> values = new List<string>();
            int index = 0;
            int maxAttempts = 10000;
            string keyWithIndex = null;

            while (index < maxAttempts)
            {
                keyWithIndex = string.Format("{0}[{1}]", key, index);

                if (!request.Params.AllKeys.Contains(keyWithIndex))
                {
                    break;
                }

                values.Add(request.GetValue(keyWithIndex));

                index += 1;
            }

            return values;
        }

        /// <summary>
        /// Tries to retrieve a list of objects from a request collection.
        /// </summary>
        /// <typeparam name="T">Value type to convert each item in the collection to.</typeparam>
        /// <param name="request">The request collection.</param>
        /// <param name="key">The key to look for in the request collection (not including the index).</param>
        /// <param name="actionDelegate">Delegate to lookup and set object properties from request collection.</param>
        /// <returns>Converted list of objects associated with the given key or an empty collection if no object values are found.</returns>
        public static IEnumerable<T> GetValues<T>(this HttpRequest request, string key, Action<T, HttpRequest, string> actionDelegate) where T : class, new()
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("key");
            }

            if (actionDelegate == null)
            {
                throw new ArgumentNullException("actionDelegate");
            }

            List<T> values = new List<T>();
            int index = 0;
            int maxAttempts = 10000;
            string keyWithIndex = null;

            while (index < maxAttempts)
            {
                keyWithIndex = string.Format("{0}[{1}]", key, index) + ".";

                if (request.Params.AllKeys.FirstOrDefault(k => k.StartsWith(keyWithIndex)) == null)
                {
                    break;
                }

                T item = new T();
                actionDelegate(item, request, keyWithIndex);
                values.Add(item);

                index += 1;
            }

            return values;
        }

        /// <summary>
        /// Determines whether a request is or is not an ajax request.
        /// </summary>
        /// <param name="request">The HTTP Request.</param>
        /// <returns>True if request is an ajax request and false otherwise.</returns>
        public static bool IsAjaxRequest(this HttpRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            return (
                request["X-Requested-With"] == "XMLHttpRequest" ||
                (
                    (request.Headers != null) && 
                    (request.Headers["X-Requested-With"] == "XMLHttpRequest")
                )
            );
        }
    }
}
